OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
首先先試試以下這段:
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
Compiling basic v0.1.0 (D:\projects\rust_learning\basic)
error[E0597]: `x` does not live long enough
--> src\main.rs:103:13
|
103 | r = &x;
| ^^ borrowed value does not live long enough
104 | }
| - `x` dropped here while still borrowed
105 |
106 | println!("r: {}", r);
| - borrow later used here
For more information about this error, try `rustc --explain E0597`.
r
要借用x
,但x
在作用域之後就drop了,compiler直接標示 x
活的不夠長,也就指向生命週期這個主題。
對於這件事,Rust會由它的借用檢查器(borrow checker)檢查這些借用是否還有效。
在函式中,最常發現我們的借用,在出了函式的作用域就消亡(drop)了。
fn longest(x: &str, y: &str) -> &str { // ERROR! 這裡會抱怨要回傳的借用離開作用域就消亡了
if x.len() > y.len() {
x
} else {
y
}
}
// ...
fn main() {
let x = longest("Monday", "Tuesday");
println!("Result: {}", x);
}
執行以上程式,會給出這樣的錯誤:
Compiling basic v0.1.0 (D:\projects\rust_learning\basic)
error[E0106]: missing lifetime specifier
--> src\main.rs:1:33
|
1 | fn longest(x: &str, y: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
|
1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
| ++++ ++ ++ ++
For more information about this error, try `rustc --explain E0106`.
然後compiler會建議在函式名稱、參數型別前面、回傳值前面,加上'a
的符號,標示他們生命週期時間,這個是下面要介紹的生命週期詮釋語法。
生命週期詮釋語法會是以'
為開頭,然後後面加上一個自己命名的變數名稱加到每個引用上,變數名稱大多會是'a'
如同上面compiler錯誤中給的建議訊息,但其實想取什麼都可以,只要明確標示這些引用都是同個關係就好:
// 這樣也是可以的...
fn longest<'alpha>(x: &'alpha str, y: &'alpha str) -> &'alpha str {
if x.len() > y.len() {
x
} else {
y
}
}
使用上面修改過的範例,是可以正常執行的,我們成功修改了上面提到的錯誤,照著上面的論述,在每個引用加上生命週期的變數,我們讓函式中的引用都標示與函式有著一樣的時間,但這個是什麼呢?在函式名稱後面像泛型的語法:
fn longest<'alpha> // <-- what is this mean ?
這句表示是宣告生命週期的變數稱為'alpha'
的意思,在longest
這個函式裡面
注意! 生命週期詮釋語法只是明確地告訴compiler借用之間生命週期的關係,並不是改變借用的生命週期(存活時間)。
知道了生命週期的詮釋,我們也可以在結構裡面使用引用型別的成員變數,標示這些引用成員是跟著Character
消亡的:
struct Character<'a> {
first_name: &'a str,
last_name: &'a str,
}
fn main() {
let character_name = String::from("Charlie Brown");
let character = Character {
first_name: &character_name[0..7],
last_name: &character_name[7..13],
};
println!(
"First name: {}, last name: {}",
character.first_name, character.last_name
);
}
前幾天有一篇在嘗試理解Rust中String跟string literal的差別,最下面有一個範例:
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes(); // The type is &[u8]
// 利用迭代器把序列的index跟element抓出來
for (index, &element) in bytes.iter().enumerate() {
if element == b' ' {
return &s[0..index];
}
}
&s[..]
}
在理解生命週期詮釋之後,就會覺得這個語法有些奇怪,回傳的是引用,那compiler為什麼沒有報錯呢,會是參數&String
的問題嗎?改成&str
看看:
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes(); // The type is &[u8]
// 利用迭代器把序列的index跟element抓出來
for (index, &element) in bytes.iter().enumerate() {
if element == b' ' {
return &s[0..index];
}
}
&s[..]
}
compiler是給過的。Why?在某些情況下可以對生命週期的詮釋進行省略。
以下則是關於Rust可以對生命週期詮釋進行省略的規則:
fn bar<'a, 'b>(x: &'a i32, y: &'b i32)
,但Rust都讓我們把它省略了,所以最後才會是可以直接這樣用fn bar(x: &i32, y: &i32)
。&str
都被賦值(assign)生命週期變數。&self
或是&mut self
的情況,所有輸出都會有生命週期變數。如同其他語言中有static
這個修飾詞,宣告為static
的變數會在程式執行期間都存活著,Rust也一樣,:
static x: i32 = 100;
然後也可以把static
用在生命週期的詮釋上:
let test: &'static str = "Hello";
因為標記為static
,這樣做會是可以的:
let x: &str;
{
let test: &'static str = "Hello";
x = test;
}
println!("{}", x);